﻿using System;
using System.Linq;
using System.Linq.Expressions;
using RatingAuthor;

namespace ConditionTreeTestStand.Nodes
{
  public class RootNodeViewModel : NodeViewModel
  {
    public RootNodeViewModel(NodeContext context)
      : base(null, context)
    {
    }

    public override void RemoveChild(NodeViewModel child)
    {
      Children.Remove(child);
      var newChild = child.Children[0];
      Children.Insert(0, newChild);
      newChild.Parent = this;
      foreach (var grand in child.Children.Skip(1))
      {
        newChild.Children.Insert(0, grand);
        grand.Parent = newChild;
      }
    }

    public event EventHandler ConditionChanged;

    public override Expression GetExpression()
    {
      return Children[0].GetExpression();
    }

    public string Id { get { return ToString(); } }

    protected internal override void OnConditionChanged()
    {
      InvokeConditionChanged(EventArgs.Empty);
      NotifyOfPropertyChange(() => Id);
    }
    public void InvokeConditionChanged(EventArgs e)
    {
      EventHandler handler = ConditionChanged;
      if (handler != null) handler(this, e);
    }

    public override bool TestBook(Book b)
    {
      return Children[0].TestBook(b);
    }

    protected override NodeViewModel CreateReplacementNode()
    {
      throw new NotImplementedException();
    }

    public override string ToString()
    {
      return Children[0].ToString();
    }

    public Func<Book, bool> Compile()
    {
      var param = Expression.Parameter(typeof(Book));
      var exp = Parametrize(GetExpression(), param);
      if (exp == null) return null;
      var lambda = Expression.Lambda<Func<Book, bool>>(exp, param);
      return lambda.Compile();
    }

    private static Expression Parametrize(Expression src, ParameterExpression param)
    {
      var binary = src as BinaryExpression;
      if (binary != null)
      {
        return Expression.MakeBinary(binary.NodeType,
          Parametrize(binary.Left, param),
          Parametrize(binary.Right, param));
      }
      var member = src as MemberExpression;
      if (member != null)
      {
        return Expression.MakeMemberAccess(
          Parametrize(member.Expression, param),
          member.Member);
      }
      var call = src as MethodCallExpression;
      if (call != null)
      {
        return Expression.Call(
          Parametrize(call.Object, param), call.Method,
          call.Arguments.Select(arg => Parametrize(arg, param)));
      }
      return src is ParameterExpression ? param : src;
    }

  }
}